home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / lib / python2.6 / mhlib.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2009-11-11  |  34KB  |  1,228 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. """MH interface -- purely object-oriented (well, almost)
  5.  
  6. Executive summary:
  7.  
  8. import mhlib
  9.  
  10. mh = mhlib.MH()         # use default mailbox directory and profile
  11. mh = mhlib.MH(mailbox)  # override mailbox location (default from profile)
  12. mh = mhlib.MH(mailbox, profile) # override mailbox and profile
  13.  
  14. mh.error(format, ...)   # print error message -- can be overridden
  15. s = mh.getprofile(key)  # profile entry (None if not set)
  16. path = mh.getpath()     # mailbox pathname
  17. name = mh.getcontext()  # name of current folder
  18. mh.setcontext(name)     # set name of current folder
  19.  
  20. list = mh.listfolders() # names of top-level folders
  21. list = mh.listallfolders() # names of all folders, including subfolders
  22. list = mh.listsubfolders(name) # direct subfolders of given folder
  23. list = mh.listallsubfolders(name) # all subfolders of given folder
  24.  
  25. mh.makefolder(name)     # create new folder
  26. mh.deletefolder(name)   # delete folder -- must have no subfolders
  27.  
  28. f = mh.openfolder(name) # new open folder object
  29.  
  30. f.error(format, ...)    # same as mh.error(format, ...)
  31. path = f.getfullname()  # folder's full pathname
  32. path = f.getsequencesfilename() # full pathname of folder's sequences file
  33. path = f.getmessagefilename(n)  # full pathname of message n in folder
  34.  
  35. list = f.listmessages() # list of messages in folder (as numbers)
  36. n = f.getcurrent()      # get current message
  37. f.setcurrent(n)         # set current message
  38. list = f.parsesequence(seq)     # parse msgs syntax into list of messages
  39. n = f.getlast()         # get last message (0 if no messagse)
  40. f.setlast(n)            # set last message (internal use only)
  41.  
  42. dict = f.getsequences() # dictionary of sequences in folder {name: list}
  43. f.putsequences(dict)    # write sequences back to folder
  44.  
  45. f.createmessage(n, fp)  # add message from file f as number n
  46. f.removemessages(list)  # remove messages in list from folder
  47. f.refilemessages(list, tofolder) # move messages in list to other folder
  48. f.movemessage(n, tofolder, ton)  # move one message to a given destination
  49. f.copymessage(n, tofolder, ton)  # copy one message to a given destination
  50.  
  51. m = f.openmessage(n)    # new open message object (costs a file descriptor)
  52. m is a derived class of mimetools.Message(rfc822.Message), with:
  53. s = m.getheadertext()   # text of message's headers
  54. s = m.getheadertext(pred) # text of message's headers, filtered by pred
  55. s = m.getbodytext()     # text of message's body, decoded
  56. s = m.getbodytext(0)    # text of message's body, not decoded
  57. """
  58. from warnings import warnpy3k
  59. warnpy3k('the mhlib module has been removed in Python 3.0; use the mailbox module instead', stacklevel = 2)
  60. del warnpy3k
  61. MH_PROFILE = '~/.mh_profile'
  62. PATH = '~/Mail'
  63. MH_SEQUENCES = '.mh_sequences'
  64. FOLDER_PROTECT = 448
  65. import os
  66. import sys
  67. import re
  68. import mimetools
  69. import multifile
  70. import shutil
  71. from bisect import bisect
  72. __all__ = [
  73.     'MH',
  74.     'Error',
  75.     'Folder',
  76.     'Message']
  77.  
  78. class Error(Exception):
  79.     pass
  80.  
  81.  
  82. class MH:
  83.     '''Class representing a particular collection of folders.
  84.     Optional constructor arguments are the pathname for the directory
  85.     containing the collection, and the MH profile to use.
  86.     If either is omitted or empty a default is used; the default
  87.     directory is taken from the MH profile if it is specified there.'''
  88.     
  89.     def __init__(self, path = None, profile = None):
  90.         '''Constructor.'''
  91.         if profile is None:
  92.             profile = MH_PROFILE
  93.         
  94.         self.profile = os.path.expanduser(profile)
  95.         if path is None:
  96.             path = self.getprofile('Path')
  97.         
  98.         if not path:
  99.             path = PATH
  100.         
  101.         if not os.path.isabs(path) and path[0] != '~':
  102.             path = os.path.join('~', path)
  103.         
  104.         path = os.path.expanduser(path)
  105.         if not os.path.isdir(path):
  106.             raise Error, 'MH() path not found'
  107.         os.path.isdir(path)
  108.         self.path = path
  109.  
  110.     
  111.     def __repr__(self):
  112.         '''String representation.'''
  113.         return 'MH(%r, %r)' % (self.path, self.profile)
  114.  
  115.     
  116.     def error(self, msg, *args):
  117.         '''Routine to print an error.  May be overridden by a derived class.'''
  118.         sys.stderr.write('MH error: %s\n' % msg % args)
  119.  
  120.     
  121.     def getprofile(self, key):
  122.         '''Return a profile entry, None if not found.'''
  123.         return pickline(self.profile, key)
  124.  
  125.     
  126.     def getpath(self):
  127.         """Return the path (the name of the collection's directory)."""
  128.         return self.path
  129.  
  130.     
  131.     def getcontext(self):
  132.         '''Return the name of the current folder.'''
  133.         context = pickline(os.path.join(self.getpath(), 'context'), 'Current-Folder')
  134.         if not context:
  135.             context = 'inbox'
  136.         
  137.         return context
  138.  
  139.     
  140.     def setcontext(self, context):
  141.         '''Set the name of the current folder.'''
  142.         fn = os.path.join(self.getpath(), 'context')
  143.         f = open(fn, 'w')
  144.         f.write('Current-Folder: %s\n' % context)
  145.         f.close()
  146.  
  147.     
  148.     def listfolders(self):
  149.         '''Return the names of the top-level folders.'''
  150.         folders = []
  151.         path = self.getpath()
  152.         for name in os.listdir(path):
  153.             fullname = os.path.join(path, name)
  154.             if os.path.isdir(fullname):
  155.                 folders.append(name)
  156.                 continue
  157.         
  158.         folders.sort()
  159.         return folders
  160.  
  161.     
  162.     def listsubfolders(self, name):
  163.         '''Return the names of the subfolders in a given folder
  164.         (prefixed with the given folder name).'''
  165.         fullname = os.path.join(self.path, name)
  166.         nlinks = os.stat(fullname).st_nlink
  167.         if nlinks <= 2:
  168.             return []
  169.         subfolders = []
  170.         subnames = os.listdir(fullname)
  171.         for subname in subnames:
  172.             fullsubname = os.path.join(fullname, subname)
  173.             if os.path.isdir(fullsubname):
  174.                 name_subname = os.path.join(name, subname)
  175.                 subfolders.append(name_subname)
  176.                 nlinks = nlinks - 1
  177.                 if nlinks <= 2:
  178.                     break
  179.                 
  180.             nlinks <= 2
  181.         
  182.         subfolders.sort()
  183.         return subfolders
  184.  
  185.     
  186.     def listallfolders(self):
  187.         '''Return the names of all folders and subfolders, recursively.'''
  188.         return self.listallsubfolders('')
  189.  
  190.     
  191.     def listallsubfolders(self, name):
  192.         '''Return the names of subfolders in a given folder, recursively.'''
  193.         fullname = os.path.join(self.path, name)
  194.         nlinks = os.stat(fullname).st_nlink
  195.         if nlinks <= 2:
  196.             return []
  197.         subfolders = []
  198.         subnames = os.listdir(fullname)
  199.         for subname in subnames:
  200.             if subname[0] == ',' or isnumeric(subname):
  201.                 continue
  202.             
  203.             fullsubname = os.path.join(fullname, subname)
  204.             if os.path.isdir(fullsubname):
  205.                 name_subname = os.path.join(name, subname)
  206.                 subfolders.append(name_subname)
  207.                 if not os.path.islink(fullsubname):
  208.                     subsubfolders = self.listallsubfolders(name_subname)
  209.                     subfolders = subfolders + subsubfolders
  210.                 
  211.                 nlinks = nlinks - 1
  212.                 if nlinks <= 2:
  213.                     break
  214.                 
  215.             nlinks <= 2
  216.         
  217.         subfolders.sort()
  218.         return subfolders
  219.  
  220.     
  221.     def openfolder(self, name):
  222.         '''Return a new Folder object for the named folder.'''
  223.         return Folder(self, name)
  224.  
  225.     
  226.     def makefolder(self, name):
  227.         '''Create a new folder (or raise os.error if it cannot be created).'''
  228.         protect = pickline(self.profile, 'Folder-Protect')
  229.         if protect and isnumeric(protect):
  230.             mode = int(protect, 8)
  231.         else:
  232.             mode = FOLDER_PROTECT
  233.         os.mkdir(os.path.join(self.getpath(), name), mode)
  234.  
  235.     
  236.     def deletefolder(self, name):
  237.         '''Delete a folder.  This removes files in the folder but not
  238.         subdirectories.  Raise os.error if deleting the folder itself fails.'''
  239.         fullname = os.path.join(self.getpath(), name)
  240.         for subname in os.listdir(fullname):
  241.             fullsubname = os.path.join(fullname, subname)
  242.             
  243.             try:
  244.                 os.unlink(fullsubname)
  245.             continue
  246.             except os.error:
  247.                 self.error('%s not deleted, continuing...' % fullsubname)
  248.                 continue
  249.             
  250.  
  251.         
  252.         os.rmdir(fullname)
  253.  
  254.  
  255. numericprog = re.compile('^[1-9][0-9]*$')
  256.  
  257. def isnumeric(str):
  258.     return numericprog.match(str) is not None
  259.  
  260.  
  261. class Folder:
  262.     '''Class representing a particular folder.'''
  263.     
  264.     def __init__(self, mh, name):
  265.         '''Constructor.'''
  266.         self.mh = mh
  267.         self.name = name
  268.         if not os.path.isdir(self.getfullname()):
  269.             raise Error, 'no folder %s' % name
  270.         os.path.isdir(self.getfullname())
  271.  
  272.     
  273.     def __repr__(self):
  274.         '''String representation.'''
  275.         return 'Folder(%r, %r)' % (self.mh, self.name)
  276.  
  277.     
  278.     def error(self, *args):
  279.         '''Error message handler.'''
  280.         self.mh.error(*args)
  281.  
  282.     
  283.     def getfullname(self):
  284.         '''Return the full pathname of the folder.'''
  285.         return os.path.join(self.mh.path, self.name)
  286.  
  287.     
  288.     def getsequencesfilename(self):
  289.         """Return the full pathname of the folder's sequences file."""
  290.         return os.path.join(self.getfullname(), MH_SEQUENCES)
  291.  
  292.     
  293.     def getmessagefilename(self, n):
  294.         '''Return the full pathname of a message in the folder.'''
  295.         return os.path.join(self.getfullname(), str(n))
  296.  
  297.     
  298.     def listsubfolders(self):
  299.         '''Return list of direct subfolders.'''
  300.         return self.mh.listsubfolders(self.name)
  301.  
  302.     
  303.     def listallsubfolders(self):
  304.         '''Return list of all subfolders.'''
  305.         return self.mh.listallsubfolders(self.name)
  306.  
  307.     
  308.     def listmessages(self):
  309.         '''Return the list of messages currently present in the folder.
  310.         As a side effect, set self.last to the last message (or 0).'''
  311.         messages = []
  312.         match = numericprog.match
  313.         append = messages.append
  314.         for name in os.listdir(self.getfullname()):
  315.             if match(name):
  316.                 append(name)
  317.                 continue
  318.         
  319.         messages = map(int, messages)
  320.         messages.sort()
  321.         if messages:
  322.             self.last = messages[-1]
  323.         else:
  324.             self.last = 0
  325.         return messages
  326.  
  327.     
  328.     def getsequences(self):
  329.         '''Return the set of sequences for the folder.'''
  330.         sequences = { }
  331.         fullname = self.getsequencesfilename()
  332.         
  333.         try:
  334.             f = open(fullname, 'r')
  335.         except IOError:
  336.             return sequences
  337.  
  338.         while None:
  339.             line = f.readline()
  340.             if not line:
  341.                 break
  342.             
  343.             fields = line.split(':')
  344.             if len(fields) != 2:
  345.                 self.error('bad sequence in %s: %s' % (fullname, line.strip()))
  346.             
  347.             key = fields[0].strip()
  348.             value = IntSet(fields[1].strip(), ' ').tolist()
  349.             sequences[key] = value
  350.             continue
  351.             return sequences
  352.  
  353.     
  354.     def putsequences(self, sequences):
  355.         '''Write the set of sequences back to the folder.'''
  356.         fullname = self.getsequencesfilename()
  357.         f = None
  358.         for key, seq in sequences.iteritems():
  359.             s = IntSet('', ' ')
  360.             s.fromlist(seq)
  361.             if not f:
  362.                 f = open(fullname, 'w')
  363.             
  364.             f.write('%s: %s\n' % (key, s.tostring()))
  365.         
  366.         if not f:
  367.             
  368.             try:
  369.                 os.unlink(fullname)
  370.             except os.error:
  371.                 pass
  372.             except:
  373.                 None<EXCEPTION MATCH>os.error
  374.             
  375.  
  376.         None<EXCEPTION MATCH>os.error
  377.         f.close()
  378.  
  379.     
  380.     def getcurrent(self):
  381.         '''Return the current message.  Raise Error when there is none.'''
  382.         seqs = self.getsequences()
  383.         
  384.         try:
  385.             return max(seqs['cur'])
  386.         except (ValueError, KeyError):
  387.             raise Error, 'no cur message'
  388.  
  389.  
  390.     
  391.     def setcurrent(self, n):
  392.         '''Set the current message.'''
  393.         updateline(self.getsequencesfilename(), 'cur', str(n), 0)
  394.  
  395.     
  396.     def parsesequence(self, seq):
  397.         '''Parse an MH sequence specification into a message list.
  398.         Attempt to mimic mh-sequence(5) as close as possible.
  399.         Also attempt to mimic observed behavior regarding which
  400.         conditions cause which error messages.'''
  401.         all = self.listmessages()
  402.         if not all:
  403.             raise Error, 'no messages in %s' % self.name
  404.         all
  405.         if seq == 'all':
  406.             return all
  407.         i = seq.find(':')
  408.         if i >= 0:
  409.             head = seq[:i]
  410.             dir = ''
  411.             tail = seq[i + 1:]
  412.             if tail[:1] in '-+':
  413.                 dir = tail[:1]
  414.                 tail = tail[1:]
  415.             
  416.             if not isnumeric(tail):
  417.                 raise Error, 'bad message list %s' % seq
  418.             isnumeric(tail)
  419.             
  420.             try:
  421.                 count = int(tail)
  422.             except (ValueError, OverflowError):
  423.                 count = len(all)
  424.  
  425.             
  426.             try:
  427.                 anchor = self._parseindex(head, all)
  428.             except Error:
  429.                 msg = None
  430.                 seqs = self.getsequences()
  431.                 if head not in seqs:
  432.                     if not msg:
  433.                         msg = 'bad message list %s' % seq
  434.                     
  435.                     raise Error, msg, sys.exc_info()[2]
  436.                 head not in seqs
  437.                 msgs = seqs[head]
  438.                 if not msgs:
  439.                     raise Error, 'sequence %s empty' % head
  440.                 msgs
  441.                 if dir == '-':
  442.                     return msgs[-count:]
  443.                 return msgs[:count]
  444.  
  445.             if not dir:
  446.                 if head in ('prev', 'last'):
  447.                     dir = '-'
  448.                 
  449.             
  450.             if dir == '-':
  451.                 i = bisect(all, anchor)
  452.                 return all[max(0, i - count):i]
  453.             i = bisect(all, anchor - 1)
  454.             return all[i:i + count]
  455.         i >= 0
  456.         i = seq.find('-')
  457.         if i >= 0:
  458.             begin = self._parseindex(seq[:i], all)
  459.             end = self._parseindex(seq[i + 1:], all)
  460.             i = bisect(all, begin - 1)
  461.             j = bisect(all, end)
  462.             r = all[i:j]
  463.             if not r:
  464.                 raise Error, 'bad message list %s' % seq
  465.             r
  466.             return r
  467.         
  468.         try:
  469.             n = self._parseindex(seq, all)
  470.         except Error:
  471.             i >= 0
  472.             msg = i >= 0
  473.             seq == 'all'
  474.             seqs = self.getsequences()
  475.             if seq not in seqs:
  476.                 if not msg:
  477.                     msg = 'bad message list %s' % seq
  478.                 
  479.                 raise Error, msg
  480.             seq not in seqs
  481.             return seqs[seq]
  482.  
  483.         if n not in all:
  484.             if isnumeric(seq):
  485.                 raise Error, "message %d doesn't exist" % n
  486.             isnumeric(seq)
  487.             raise Error, 'no %s message' % seq
  488.         n not in all
  489.         return [
  490.             n]
  491.  
  492.     
  493.     def _parseindex(self, seq, all):
  494.         '''Internal: parse a message number (or cur, first, etc.).'''
  495.         if isnumeric(seq):
  496.             
  497.             try:
  498.                 return int(seq)
  499.             except (OverflowError, ValueError):
  500.                 return sys.maxint
  501.             
  502.  
  503.         None<EXCEPTION MATCH>(OverflowError, ValueError)
  504.         if seq in ('cur', '.'):
  505.             return self.getcurrent()
  506.         if seq == 'first':
  507.             return all[0]
  508.         seq == 'last' if seq == 'last' else seq == 'last'
  509.         if seq == 'prev':
  510.             n = self.getcurrent()
  511.             i = bisect(all, n - 1)
  512.             if i == 0:
  513.                 raise Error, 'no prev message'
  514.             i == 0
  515.             
  516.             try:
  517.                 return all[i - 1]
  518.             except IndexError:
  519.                 seq == 'first'
  520.                 seq == 'first'
  521.                 seq in ('cur', '.')
  522.                 raise Error, 'no prev message'
  523.             except:
  524.                 seq == 'first'<EXCEPTION MATCH>IndexError
  525.             
  526.  
  527.         seq == 'first'
  528.         raise Error, None
  529.  
  530.     
  531.     def openmessage(self, n):
  532.         '''Open a message -- returns a Message object.'''
  533.         return Message(self, n)
  534.  
  535.     
  536.     def removemessages(self, list):
  537.         '''Remove one or more messages -- may raise os.error.'''
  538.         errors = []
  539.         deleted = []
  540.         for n in list:
  541.             path = self.getmessagefilename(n)
  542.             commapath = self.getmessagefilename(',' + str(n))
  543.             
  544.             try:
  545.                 os.unlink(commapath)
  546.             except os.error:
  547.                 pass
  548.  
  549.             
  550.             try:
  551.                 os.rename(path, commapath)
  552.             except os.error:
  553.                 msg = None
  554.                 errors.append(msg)
  555.                 continue
  556.  
  557.             deleted.append(n)
  558.         
  559.         if deleted:
  560.             self.removefromallsequences(deleted)
  561.         
  562.         if errors:
  563.             if len(errors) == 1:
  564.                 raise os.error, errors[0]
  565.             len(errors) == 1
  566.             raise os.error, ('multiple errors:', errors)
  567.         errors
  568.  
  569.     
  570.     def refilemessages(self, list, tofolder, keepsequences = 0):
  571.         """Refile one or more messages -- may raise os.error.
  572.         'tofolder' is an open folder object."""
  573.         errors = []
  574.         refiled = { }
  575.         for n in list:
  576.             ton = tofolder.getlast() + 1
  577.             path = self.getmessagefilename(n)
  578.             topath = tofolder.getmessagefilename(ton)
  579.             
  580.             try:
  581.                 os.rename(path, topath)
  582.             except os.error:
  583.                 
  584.                 try:
  585.                     shutil.copy2(path, topath)
  586.                     os.unlink(path)
  587.                 except (IOError, os.error):
  588.                     msg = None
  589.                     errors.append(msg)
  590.                     
  591.                     try:
  592.                         os.unlink(topath)
  593.                     continue
  594.                     except os.error:
  595.                         continue
  596.                     
  597.  
  598.                 except:
  599.                     None<EXCEPTION MATCH>(IOError, os.error)
  600.                 
  601.  
  602.                 None<EXCEPTION MATCH>(IOError, os.error)
  603.  
  604.             tofolder.setlast(ton)
  605.             refiled[n] = ton
  606.         
  607.         if refiled:
  608.             if keepsequences:
  609.                 tofolder._copysequences(self, refiled.items())
  610.             
  611.             self.removefromallsequences(refiled.keys())
  612.         
  613.         if errors:
  614.             if len(errors) == 1:
  615.                 raise os.error, errors[0]
  616.             len(errors) == 1
  617.             raise os.error, ('multiple errors:', errors)
  618.         errors
  619.  
  620.     
  621.     def _copysequences(self, fromfolder, refileditems):
  622.         '''Helper for refilemessages() to copy sequences.'''
  623.         fromsequences = fromfolder.getsequences()
  624.         tosequences = self.getsequences()
  625.         changed = 0
  626.         for name, seq in fromsequences.items():
  627.             
  628.             try:
  629.                 toseq = tosequences[name]
  630.                 new = 0
  631.             except KeyError:
  632.                 toseq = []
  633.                 new = 1
  634.  
  635.             for fromn, ton in refileditems:
  636.                 if fromn in seq:
  637.                     toseq.append(ton)
  638.                     changed = 1
  639.                     continue
  640.             
  641.             if new and toseq:
  642.                 tosequences[name] = toseq
  643.                 continue
  644.         
  645.         if changed:
  646.             self.putsequences(tosequences)
  647.         
  648.  
  649.     
  650.     def movemessage(self, n, tofolder, ton):
  651.         '''Move one message over a specific destination message,
  652.         which may or may not already exist.'''
  653.         path = self.getmessagefilename(n)
  654.         f = open(path)
  655.         f.close()
  656.         del f
  657.         topath = tofolder.getmessagefilename(ton)
  658.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  659.         
  660.         try:
  661.             os.rename(topath, backuptopath)
  662.         except os.error:
  663.             pass
  664.  
  665.         
  666.         try:
  667.             os.rename(path, topath)
  668.         except os.error:
  669.             ok = 0
  670.             
  671.             try:
  672.                 tofolder.setlast(None)
  673.                 shutil.copy2(path, topath)
  674.                 ok = 1
  675.             finally:
  676.                 if not ok:
  677.                     
  678.                     try:
  679.                         os.unlink(topath)
  680.                     except os.error:
  681.                         pass
  682.                     except:
  683.                         None<EXCEPTION MATCH>os.error
  684.                     
  685.  
  686.  
  687.             os.unlink(path)
  688.  
  689.         self.removefromallsequences([
  690.             n])
  691.  
  692.     
  693.     def copymessage(self, n, tofolder, ton):
  694.         '''Copy one message over a specific destination message,
  695.         which may or may not already exist.'''
  696.         path = self.getmessagefilename(n)
  697.         f = open(path)
  698.         f.close()
  699.         del f
  700.         topath = tofolder.getmessagefilename(ton)
  701.         backuptopath = tofolder.getmessagefilename(',%d' % ton)
  702.         
  703.         try:
  704.             os.rename(topath, backuptopath)
  705.         except os.error:
  706.             pass
  707.  
  708.         ok = 0
  709.         
  710.         try:
  711.             tofolder.setlast(None)
  712.             shutil.copy2(path, topath)
  713.             ok = 1
  714.         finally:
  715.             if not ok:
  716.                 
  717.                 try:
  718.                     os.unlink(topath)
  719.                 except os.error:
  720.                     pass
  721.                 except:
  722.                     None<EXCEPTION MATCH>os.error
  723.                 
  724.  
  725.  
  726.  
  727.     
  728.     def createmessage(self, n, txt):
  729.         '''Create a message, with text from the open file txt.'''
  730.         path = self.getmessagefilename(n)
  731.         backuppath = self.getmessagefilename(',%d' % n)
  732.         
  733.         try:
  734.             os.rename(path, backuppath)
  735.         except os.error:
  736.             pass
  737.  
  738.         ok = 0
  739.         BUFSIZE = 16384
  740.         
  741.         try:
  742.             f = open(path, 'w')
  743.             while None:
  744.                 buf = txt.read(BUFSIZE)
  745.                 if not buf:
  746.                     break
  747.                 
  748.                 continue
  749.                 f.close()
  750.                 ok = 1
  751.             if not ok:
  752.                 
  753.                 try:
  754.                     os.unlink(path)
  755.                 except os.error:
  756.                     pass
  757.                 except:
  758.                     None<EXCEPTION MATCH>os.error
  759.                 
  760.  
  761.             return None
  762.  
  763.  
  764.     
  765.     def removefromallsequences(self, list):
  766.         """Remove one or more messages from all sequences (including last)
  767.         -- but not from 'cur'!!!"""
  768.         if hasattr(self, 'last') and self.last in list:
  769.             del self.last
  770.         
  771.         sequences = self.getsequences()
  772.         changed = 0
  773.         for name, seq in sequences.items():
  774.             if name == 'cur':
  775.                 continue
  776.             
  777.             for n in list:
  778.                 if n in seq:
  779.                     seq.remove(n)
  780.                     changed = 1
  781.                     if not seq:
  782.                         del sequences[name]
  783.                     
  784.                 seq
  785.             
  786.         
  787.         if changed:
  788.             self.putsequences(sequences)
  789.         
  790.  
  791.     
  792.     def getlast(self):
  793.         '''Return the last message number.'''
  794.         if not hasattr(self, 'last'):
  795.             self.listmessages()
  796.         
  797.         return self.last
  798.  
  799.     
  800.     def setlast(self, last):
  801.         '''Set the last message number.'''
  802.         if last is None:
  803.             if hasattr(self, 'last'):
  804.                 del self.last
  805.             
  806.         else:
  807.             self.last = last
  808.  
  809.  
  810.  
  811. class Message(mimetools.Message):
  812.     
  813.     def __init__(self, f, n, fp = None):
  814.         '''Constructor.'''
  815.         self.folder = f
  816.         self.number = n
  817.         if fp is None:
  818.             path = f.getmessagefilename(n)
  819.             fp = open(path, 'r')
  820.         
  821.         mimetools.Message.__init__(self, fp)
  822.  
  823.     
  824.     def __repr__(self):
  825.         '''String representation.'''
  826.         return 'Message(%s, %s)' % (repr(self.folder), self.number)
  827.  
  828.     
  829.     def getheadertext(self, pred = None):
  830.         """Return the message's header text as a string.  If an
  831.         argument is specified, it is used as a filter predicate to
  832.         decide which headers to return (its argument is the header
  833.         name converted to lower case)."""
  834.         if pred is None:
  835.             return ''.join(self.headers)
  836.         headers = []
  837.         hit = 0
  838.         for line in self.headers:
  839.             if not line[0].isspace():
  840.                 i = line.find(':')
  841.                 if i > 0:
  842.                     hit = pred(line[:i].lower())
  843.                 
  844.             
  845.             if hit:
  846.                 headers.append(line)
  847.                 continue
  848.         
  849.         return ''.join(headers)
  850.  
  851.     
  852.     def getbodytext(self, decode = 1):
  853.         """Return the message's body text as string.  This undoes a
  854.         Content-Transfer-Encoding, but does not interpret other MIME
  855.         features (e.g. multipart messages).  To suppress decoding,
  856.         pass 0 as an argument."""
  857.         self.fp.seek(self.startofbody)
  858.         encoding = self.getencoding()
  859.         if not decode or encoding in ('', '7bit', '8bit', 'binary'):
  860.             return self.fp.read()
  861.         
  862.         try:
  863.             StringIO = StringIO
  864.             import cStringIO
  865.         except ImportError:
  866.             encoding in ('', '7bit', '8bit', 'binary')
  867.             encoding in ('', '7bit', '8bit', 'binary')
  868.             StringIO = StringIO
  869.             import StringIO
  870.         except:
  871.             encoding in ('', '7bit', '8bit', 'binary')
  872.  
  873.         output = StringIO()
  874.         mimetools.decode(self.fp, output, encoding)
  875.         return output.getvalue()
  876.  
  877.     
  878.     def getbodyparts(self):
  879.         """Only for multipart messages: return the message's body as a
  880.         list of SubMessage objects.  Each submessage object behaves
  881.         (almost) as a Message object."""
  882.         if self.getmaintype() != 'multipart':
  883.             raise Error, 'Content-Type is not multipart/*'
  884.         self.getmaintype() != 'multipart'
  885.         bdry = self.getparam('boundary')
  886.         if not bdry:
  887.             raise Error, 'multipart/* without boundary param'
  888.         bdry
  889.         self.fp.seek(self.startofbody)
  890.         mf = multifile.MultiFile(self.fp)
  891.         mf.push(bdry)
  892.         parts = []
  893.         while mf.next():
  894.             n = '%s.%r' % (self.number, 1 + len(parts))
  895.             part = SubMessage(self.folder, n, mf)
  896.             parts.append(part)
  897.         mf.pop()
  898.         return parts
  899.  
  900.     
  901.     def getbody(self):
  902.         '''Return body, either a string or a list of messages.'''
  903.         if self.getmaintype() == 'multipart':
  904.             return self.getbodyparts()
  905.         return self.getbodytext()
  906.  
  907.  
  908.  
  909. class SubMessage(Message):
  910.     
  911.     def __init__(self, f, n, fp):
  912.         '''Constructor.'''
  913.         Message.__init__(self, f, n, fp)
  914.         if self.getmaintype() == 'multipart':
  915.             self.body = Message.getbodyparts(self)
  916.         else:
  917.             self.body = Message.getbodytext(self)
  918.         self.bodyencoded = Message.getbodytext(self, decode = 0)
  919.  
  920.     
  921.     def __repr__(self):
  922.         '''String representation.'''
  923.         f = self.folder
  924.         n = self.number
  925.         fp = self.fp
  926.         return 'SubMessage(%s, %s, %s)' % (f, n, fp)
  927.  
  928.     
  929.     def getbodytext(self, decode = 1):
  930.         if not decode:
  931.             return self.bodyencoded
  932.         if type(self.body) == type(''):
  933.             return self.body
  934.  
  935.     
  936.     def getbodyparts(self):
  937.         if type(self.body) == type([]):
  938.             return self.body
  939.  
  940.     
  941.     def getbody(self):
  942.         return self.body
  943.  
  944.  
  945.  
  946. class IntSet:
  947.     """Class implementing sets of integers.
  948.  
  949.     This is an efficient representation for sets consisting of several
  950.     continuous ranges, e.g. 1-100,200-400,402-1000 is represented
  951.     internally as a list of three pairs: [(1,100), (200,400),
  952.     (402,1000)].  The internal representation is always kept normalized.
  953.  
  954.     The constructor has up to three arguments:
  955.     - the string used to initialize the set (default ''),
  956.     - the separator between ranges (default ',')
  957.     - the separator between begin and end of a range (default '-')
  958.     The separators must be strings (not regexprs) and should be different.
  959.  
  960.     The tostring() function yields a string that can be passed to another
  961.     IntSet constructor; __repr__() is a valid IntSet constructor itself.
  962.     """
  963.     
  964.     def __init__(self, data = None, sep = ',', rng = '-'):
  965.         self.pairs = []
  966.         self.sep = sep
  967.         self.rng = rng
  968.         if data:
  969.             self.fromstring(data)
  970.         
  971.  
  972.     
  973.     def reset(self):
  974.         self.pairs = []
  975.  
  976.     
  977.     def __cmp__(self, other):
  978.         return cmp(self.pairs, other.pairs)
  979.  
  980.     
  981.     def __hash__(self):
  982.         return hash(self.pairs)
  983.  
  984.     
  985.     def __repr__(self):
  986.         return 'IntSet(%r, %r, %r)' % (self.tostring(), self.sep, self.rng)
  987.  
  988.     
  989.     def normalize(self):
  990.         self.pairs.sort()
  991.         i = 1
  992.         while i < len(self.pairs):
  993.             (alo, ahi) = self.pairs[i - 1]
  994.             (blo, bhi) = self.pairs[i]
  995.             if ahi >= blo - 1:
  996.                 self.pairs[i - 1:i + 1] = [
  997.                     (alo, max(ahi, bhi))]
  998.                 continue
  999.             i = i + 1
  1000.  
  1001.     
  1002.     def tostring(self):
  1003.         s = ''
  1004.         for lo, hi in self.pairs:
  1005.             if lo == hi:
  1006.                 t = repr(lo)
  1007.             else:
  1008.                 t = repr(lo) + self.rng + repr(hi)
  1009.             if s:
  1010.                 s = s + self.sep + t
  1011.                 continue
  1012.             s = t
  1013.         
  1014.         return s
  1015.  
  1016.     
  1017.     def tolist(self):
  1018.         l = []
  1019.         for lo, hi in self.pairs:
  1020.             m = range(lo, hi + 1)
  1021.             l = l + m
  1022.         
  1023.         return l
  1024.  
  1025.     
  1026.     def fromlist(self, list):
  1027.         for i in list:
  1028.             self.append(i)
  1029.         
  1030.  
  1031.     
  1032.     def clone(self):
  1033.         new = IntSet()
  1034.         new.pairs = self.pairs[:]
  1035.         return new
  1036.  
  1037.     
  1038.     def min(self):
  1039.         return self.pairs[0][0]
  1040.  
  1041.     
  1042.     def max(self):
  1043.         return self.pairs[-1][-1]
  1044.  
  1045.     
  1046.     def contains(self, x):
  1047.         for lo, hi in self.pairs:
  1048.             if x <= x:
  1049.                 pass
  1050.             elif x <= hi:
  1051.                 return True
  1052.         
  1053.         return False
  1054.  
  1055.     
  1056.     def append(self, x):
  1057.         for i in range(len(self.pairs)):
  1058.             (lo, hi) = self.pairs[i]
  1059.             if x < lo:
  1060.                 if x + 1 == lo:
  1061.                     self.pairs[i] = (x, hi)
  1062.                 else:
  1063.                     self.pairs.insert(i, (x, x))
  1064.                 if i > 0 and x - 1 == self.pairs[i - 1][1]:
  1065.                     self.pairs[i - 1:i + 1] = [
  1066.                         (self.pairs[i - 1][0], self.pairs[i][1])]
  1067.                 
  1068.                 return None
  1069.             if x <= hi:
  1070.                 return None
  1071.         
  1072.         i = len(self.pairs) - 1
  1073.         self.pairs.append((x, x))
  1074.  
  1075.     
  1076.     def addpair(self, xlo, xhi):
  1077.         if xlo > xhi:
  1078.             return None
  1079.         self.pairs.append((xlo, xhi))
  1080.         self.normalize()
  1081.  
  1082.     
  1083.     def fromstring(self, data):
  1084.         new = []
  1085.         for part in data.split(self.sep):
  1086.             list = []
  1087.             for subp in part.split(self.rng):
  1088.                 s = subp.strip()
  1089.                 list.append(int(s))
  1090.             
  1091.             if len(list) == 1:
  1092.                 new.append((list[0], list[0]))
  1093.                 continue
  1094.             if len(list) == 2 and list[0] <= list[1]:
  1095.                 new.append((list[0], list[1]))
  1096.                 continue
  1097.             raise ValueError, 'bad data passed to IntSet'
  1098.         
  1099.         self.pairs = self.pairs + new
  1100.         self.normalize()
  1101.  
  1102.  
  1103.  
  1104. def pickline(file, key, casefold = 1):
  1105.     
  1106.     try:
  1107.         f = open(file, 'r')
  1108.     except IOError:
  1109.         return None
  1110.  
  1111.     pat = re.escape(key) + ':'
  1112.     if casefold:
  1113.         pass
  1114.     prog = re.compile(pat, re.IGNORECASE)
  1115.     while None:
  1116.         line = f.readline()
  1117.         if not line:
  1118.             break
  1119.         
  1120.         if prog.match(line):
  1121.             text = line[len(key) + 1:]
  1122.             while None:
  1123.                 line = f.readline()
  1124.                 if not line or not line[0].isspace():
  1125.                     break
  1126.                 
  1127.                 text = text + line
  1128.                 continue
  1129.                 return text.strip()
  1130.                 continue
  1131.                 return None
  1132.  
  1133.  
  1134. def updateline(file, key, value, casefold = 1):
  1135.     
  1136.     try:
  1137.         f = open(file, 'r')
  1138.         lines = f.readlines()
  1139.         f.close()
  1140.     except IOError:
  1141.         lines = []
  1142.  
  1143.     pat = re.escape(key) + ':(.*)\n'
  1144.     if casefold:
  1145.         pass
  1146.     prog = re.compile(pat, re.IGNORECASE)
  1147.     if value is None:
  1148.         newline = None
  1149.     else:
  1150.         newline = '%s: %s\n' % (key, value)
  1151.     for i in range(len(lines)):
  1152.         line = lines[i]
  1153.         if prog.match(line):
  1154.             if newline is None:
  1155.                 del lines[i]
  1156.             else:
  1157.                 lines[i] = newline
  1158.             break
  1159.             continue
  1160.     elif newline is not None:
  1161.         lines.append(newline)
  1162.     
  1163.     tempfile = file + '~'
  1164.     f = open(tempfile, 'w')
  1165.     for line in lines:
  1166.         f.write(line)
  1167.     
  1168.     f.close()
  1169.     os.rename(tempfile, file)
  1170.  
  1171.  
  1172. def test():
  1173.     global mh, f
  1174.     os.system('rm -rf $HOME/Mail/@test')
  1175.     mh = MH()
  1176.     
  1177.     def do(s):
  1178.         print s
  1179.         print eval(s)
  1180.  
  1181.     do('mh.listfolders()')
  1182.     do('mh.listallfolders()')
  1183.     testfolders = [
  1184.         '@test',
  1185.         '@test/test1',
  1186.         '@test/test2',
  1187.         '@test/test1/test11',
  1188.         '@test/test1/test12',
  1189.         '@test/test1/test11/test111']
  1190.     for t in testfolders:
  1191.         do('mh.makefolder(%r)' % (t,))
  1192.     
  1193.     do("mh.listsubfolders('@test')")
  1194.     do("mh.listallsubfolders('@test')")
  1195.     f = mh.openfolder('@test')
  1196.     do('f.listsubfolders()')
  1197.     do('f.listallsubfolders()')
  1198.     do('f.getsequences()')
  1199.     seqs = f.getsequences()
  1200.     seqs['foo'] = IntSet('1-10 12-20', ' ').tolist()
  1201.     print seqs
  1202.     f.putsequences(seqs)
  1203.     do('f.getsequences()')
  1204.     for t in reversed(testfolders):
  1205.         do('mh.deletefolder(%r)' % (t,))
  1206.     
  1207.     do('mh.getcontext()')
  1208.     context = mh.getcontext()
  1209.     f = mh.openfolder(context)
  1210.     do('f.getcurrent()')
  1211.     for seq in ('first', 'last', 'cur', '.', 'prev', 'next', 'first:3', 'last:3', 'cur:3', 'cur:-3', 'prev:3', 'next:3', '1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3', 'all'):
  1212.         
  1213.         try:
  1214.             do('f.parsesequence(%r)' % (seq,))
  1215.         except Error:
  1216.             msg = None
  1217.             print 'Error:', msg
  1218.  
  1219.         stuff = os.popen('pick %r 2>/dev/null' % (seq,)).read()
  1220.         list = map(int, stuff.split())
  1221.         print list, '<-- pick'
  1222.     
  1223.     do('f.listmessages()')
  1224.  
  1225. if __name__ == '__main__':
  1226.     test()
  1227.  
  1228.